package com.bom.core.util.db;

import cn.hutool.core.io.resource.ResourceUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.StrUtil;
import com.bom.core.util.key.Key;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.experimental.UtilityClass;
import lombok.extern.log4j.Log4j2;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.ResultMap;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.mapping.SqlSource;
import org.apache.ibatis.scripting.xmltags.XMLLanguageDriver;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import java.io.StringReader;
import java.util.*;
import java.util.stream.Collectors;

@UtilityClass
@Log4j2
public class Mybatis {
    private Map<String, MybatisModel> map = new LinkedHashMap<>();

    public MybatisModel builder(MybatisKey key, Properties properties, StringReader... config) {
        MybatisModel model = new MybatisModel(key, properties, config);
        map.put(key.getKey(), model);
        return model;
    }


    /**
     * 模型的配置
     */
    public static class MybatisModel {
        private final MybatisKey key;
        private final SqlSessionFactoryBuilder sqlSessionFactoryBuilder;
        private final StringReader config;
        private final Properties properties;
        @Getter
        private final SqlSessionFactory sqlSessionFactory;

        /**
         * 构建模型并初始化
         */
        private MybatisModel(MybatisKey key, Properties properties, StringReader... config) {
            this.key = key;
            this.sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
            this.config = ArrayUtil.isEmpty(config) ? new StringReader(ResourceUtil.readUtf8Str("easy/mybatis.xml")) : config[0];
            this.properties = properties;
            this.sqlSessionFactory = sqlSessionFactoryBuilder.build(this.config, "default", properties);//新建
        }

        public void load(String id, String xml, Class<?> type) {
            Configuration configuration = sqlSessionFactory.getConfiguration();
            //构建标志
            MappedStatement mappedStatement;
            try {
                configuration.getMappedStatement(id);//是否已经注入
            } catch (Exception e) {//判断是否注册
                log.warn("未注册:{}-->{}", id, e.getMessage());
                XMLLanguageDriver xmlLanguageDriver = new XMLLanguageDriver();
                SqlSource sqlSource = xmlLanguageDriver.createSqlSource(configuration, xml, Map.class);
                MappedStatement.Builder mappedStatementBuilder = new MappedStatement.Builder(configuration, id, sqlSource, SqlCommandType.UNKNOWN);
                LinkedList<ResultMap> resultMaps = new LinkedList<>();
                resultMaps.add(new ResultMap.Builder(
                        configuration,
                        id + "_resultType",
                        type,
                        new ArrayList<>(),
                        null).build());
                mappedStatementBuilder.resultMaps(resultMaps);
                mappedStatement = mappedStatementBuilder.build();
                configuration.addMappedStatement(mappedStatement);//注入sql
            }
        }

        public List<Map<String, Object>> list(String id, String sql, Object dto) {
            load(id, sql, Map.class);
            //判断分页
            SqlSession sqlSession = sqlSessionFactory.openSession();
            try {
                return sqlSession.selectList(id, dto);
            } finally {
                sqlSession.commit();
                sqlSession.close();
            }
        }

        public Page<Map<String, Object>> page(String id, String sql, Object dto, Integer num, Integer size) {
            load(id, sql, Map.class);
            //判断分页
            SqlSession sqlSession = sqlSessionFactory.openSession();
            try {
                return PageHelper.startPage(num, size).doSelectPage(() -> {
                    sqlSession.selectList(id, dto);
                });
            } finally {
                sqlSession.commit();
                sqlSession.close();
            }
        }

        /**
         * 查询详情
         */
        public Map<String, Object> detail(String id, String sql, Object dto) {
            load(id, sql, Map.class);
            //判断分页
            SqlSession sqlSession = sqlSessionFactory.openSession();
            try {
                log.info("详情查询");
                return sqlSession.selectOne(id, dto);
            } finally {
                sqlSession.commit();
                sqlSession.close();
            }
        }

        public Integer update(String id, String sql, Object dto) {
            if (StrUtil.isNotBlank(sql)) {
                load(id, sql, Integer.class);
            }
            //判断分页
            SqlSession sqlSession = sqlSessionFactory.openSession();
            try {
                log.info("详情查询");
                return sqlSession.update(id, dto);
            } finally {
                sqlSession.commit();
                sqlSession.close();
            }
        }

        public int execute(String id, String sql, Object dto) {
            load(id, sql, String.class);
            SqlSession sqlSession = sqlSessionFactory.openSession();
            try {
                return sqlSession.update(id, dto);
            } finally {
                sqlSession.commit();
                sqlSession.close();
            }
        }

        public Integer createTb(TbFactory.Tb tb) {//最自动注入一些sql
            String createTbSql = TbFactory.createTb(tb);
            String id = "createTb_" + tb.getName();
            load(id, createTbSql, Integer.class);
            SqlSession sqlSession = sqlSessionFactory.openSession();
            try {
                return sqlSession.update(id, tb);
            } catch (Exception e) {
                log.error(e.getMessage());
                return null;
            } finally {
                sqlSession.commit();
                sqlSession.close();
                //构建所有的查询语句
                Map<String, TbFactory.Index> indexsMap = tb.getIndexsMap();
                for (Map.Entry<String, TbFactory.Index> IndexEntry : indexsMap.entrySet()) {
                    TbFactory.Index value = IndexEntry.getValue();
                    List<TbFactory.IndexNode> nodes = value.getNodes();
                    String conditionStr = nodes.stream().map(e -> StrUtil.upperFirst(e.getName())).collect(Collectors.joining("And"));
                    String condition = nodes.stream().map(e -> e.getName() + "=#{" + e.getName() + "}").collect(Collectors.joining(" and "));
                    String sql = "<script> update " + tb.getName() + " set\n" +
                            "    <foreach collection=\"_parameter.entrySet()\" index=\"key\" item=\"value\" separator=\",\">\n" +
                            "        <if test=\"value != null\">\n" +
                            "            ${key}=#{value}\n" +
                            "        </if>\n" +
                            "    </foreach>\n" +
                            "where " + condition + " </script>";
                    load("update" + StrUtil.upperFirst(tb.getName()) + "Where" + conditionStr, sql, Integer.class);
                }
            }
        }
    }

    @EqualsAndHashCode(callSuper = true)
    @Data
    public static class MybatisKey extends Key {
        public MybatisKey(String key, String value) {
            super(key, value);
        }
    }
}
